package com.wiseman.writing.utility;

import java.util.List;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Path;
import android.graphics.Path.FillType;
import android.graphics.Point;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;

import com.wiseman.writing.ConstValue;

public class StrokeView extends View implements ConstValue {

	private Bitmap mDemoBitmap = null;
	private Bitmap mWriteBitmap = null;

	private Canvas mDemoCanvas = null;
	private Canvas mWriteCanvas = null;
	private Paint mFillBlackPaint = null;
	private Paint mStrokeGrayPaint = null;
	private Paint mFillWhitePaint = null;

	private BSJavaApi mBSObj = null;

	private List<StrokeData> mStrokeDataList = null;

	private int mStrokeNum = 0;
	private int mFontSize = -1;
	private char mCharacter = 0xfffe;

	int mDemo_x = 0;
	int mDemo_y = 0;
	int mWrite_x = 0;
	int mWrite_y = 0;

	Writing mWriting;

	int m_DemoStep = DEFAULT_DEMO_STEP;
	int m_DemoDelay = DEFAULT_DEMO_DELAY;

	public enum StrokeMode {
		demo, writing, strokeSeq,
	};

	StrokeMode mStrokeMode = StrokeMode.demo;
	private boolean mIsDemoComplete = true;
	private boolean mIsDemoBreak = false;

	// for list.
	String mStrChars = null;
	int mCharsIndex = 0;

	public interface Listener {
		void finishedWriting();
	}

	Listener mListener = null;
	private int mTouchSlopSquare;

	public StrokeView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		defInit();
	}

	public StrokeView(Context context, AttributeSet attrs) {
		super(context, attrs);
		defInit();
	}

	public StrokeView(Context context) {
		super(context);
		defInit();
	}

	void defInit() {
		mBSObj = new BSJavaApi(getContext(), 1, BSFTABTYPE_UNICODEBYGB);
		init();
	}

	public StrokeView(Context context, int fontSize, StrokeMode mode) {
		super(context, null);
		mBSObj = new BSJavaApi(getContext(), 1, BSFTABTYPE_UNICODEBYGB);
		init();
		mStrokeMode = mode;
	}

	public void setListener(Listener listener) {
		mListener = listener;
	}

	public void setMode(StrokeMode mode) {
		mStrokeMode = mode;
	}

	public void clear() {
		if (mDemoBitmap != null) {
			mDemoBitmap.recycle();
			mDemoBitmap = null;
		}
		if (mWriteBitmap != null) {
			mWriteBitmap.recycle();
			mWriteBitmap = null;
		}

		mStrokeDataList.clear();

		System.gc();

	}

	private void setFontSize(int size) {

		if (mFontSize == size)
			return;

		mWriting.setWritingSensivity(3);

		mFontSize = size;

		if (mDemoBitmap != null) {
			mDemoBitmap.recycle();
		}

		mDemoBitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
		mDemoCanvas.setBitmap(mDemoBitmap);

		if (mWriteBitmap != null) {
			mWriteBitmap.recycle();
		}

		mWriteBitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
		mWriteCanvas.setBitmap(mWriteBitmap);
	}

	private void init() {
		mDemo_x = 0;
		mDemo_y = 0;
		mWrite_x = 0;
		mWrite_y = 0;

		mFillWhitePaint = new Paint();
		mFillWhitePaint.setColor(Color.WHITE);
		mFillWhitePaint.setStyle(Style.FILL);

		mStrokeGrayPaint = new Paint();
		mStrokeGrayPaint.setColor(Color.GRAY);
		mStrokeGrayPaint.setStyle(Style.STROKE);
		mStrokeGrayPaint.setAntiAlias(true);

		mFillBlackPaint = new Paint();
		mFillBlackPaint.setColor(Color.BLACK);
		mFillBlackPaint.setStyle(Style.FILL);
		mFillBlackPaint.setAntiAlias(true);

		mDemoCanvas = new Canvas();
		mWriteCanvas = new Canvas();

		mWriting = new Writing(this);

		final ViewConfiguration configuration = ViewConfiguration.get(getContext());
		int touchSlop = configuration.getScaledTouchSlop();
		mTouchSlopSquare = touchSlop * touchSlop;
	}

	@Override
	protected void onDraw(Canvas canvas) {
		// canvas.draw
		Bitmap drawBmp = mDemoBitmap;

		if (drawBmp == null)
			return;

		int x = (getWidth() - drawBmp.getWidth()) / 2;
		int y = (getHeight() - drawBmp.getHeight()) / 2;

		mDemo_x = x;
		mDemo_y = y;
		mWrite_x = x;
		mWrite_y = y;

		switch (mStrokeMode) {
		case demo:
			drawBmp = mDemoBitmap;
			break;
		case writing:
			drawBmp = mWriteBitmap;
			break;
		default:
			drawBmp = mDemoBitmap;
			break;
		}

		if (drawBmp != null)
			canvas.drawBitmap(drawBmp, x, y, null);
	}

	boolean mAlwaysInTapRegion = false;
	private MotionEvent mCurrentDownEvent;

	@Override
	public boolean onTouchEvent(MotionEvent event) {

		Point pt = new Point();

		pt.x = (int) event.getX() - mWrite_x;
		pt.y = (int) event.getY() - mWrite_y;

		if (mStrokeMode != StrokeMode.writing) {

			if (mWriting.isStartWrite(pt)) {
				writeChar();
			} else {
				// demoChar();
				// return true;
				// return super.onTouchEvent(event);
			}
		}

		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
			if (mWriting.isCharComplete()) {
				showChar(null, mCharacter, mFontSize);
				mWriting.reset();
				invalidate();
			}
			mWriting.handleActionDown(pt);
			mAlwaysInTapRegion = true;
			mCurrentDownEvent = MotionEvent.obtain(event);
			break;
		case MotionEvent.ACTION_MOVE:
			mWriting.handleActionMove(pt);
			final int deltaX = (int) (event.getX() - mCurrentDownEvent.getX());
			final int deltaY = (int) (event.getY() - mCurrentDownEvent.getY());
			int distance = (deltaX * deltaX) + (deltaY * deltaY);
			if (distance > mTouchSlopSquare) {
				mAlwaysInTapRegion = false;
			}
			break;
		case MotionEvent.ACTION_UP:
			mWriting.handleActionUp(pt);
			if (mWriting.isCharComplete()) {
				if (mListener != null) {
					mListener.finishedWriting();
				}
			}

			if (mAlwaysInTapRegion) {
				demoChar();
			}
			break;
		}

		return true;
	}

	public boolean hasChar(char code) {
		if (mBSObj.prepareData(code) == false) {
			Log.d("stroke_order", "prepareData() error!");
			return false;
		}

		return true;
	}

	public boolean setChar(char code, int size) {
		Log.d("stroke_order", "set character:" + String.valueOf(code));

		if (mBSObj.prepareData(code) == false) {
			Log.d("stroke_order", "prepareData() error!");
			return false;
		}

		mStrokeNum = mBSObj.getStrokeNum();

		mCharacter = code;

		setFontSize(size);

		mStrokeDataList = mBSObj.getCharData(size);

		mWriting.reset();
		mWriting.setData(mStrokeDataList, mWriteCanvas, mFillBlackPaint);
		return true;
	}

	public void demoChar() {
		if (mIsDemoComplete == false) {
			return;
		}

		mStrokeMode = StrokeMode.demo;

		mIsDemoComplete = false;

		showChar(null, mCharacter, mFontSize);

		new Thread(new Runnable() {
			@Override
			public void run() {
				for (StrokeData data : mStrokeDataList) {
					if (mIsDemoBreak) {
						mIsDemoBreak = false;
						break;
					}
					demoStroke(data);
				}

				mIsDemoComplete = true;
			}
		}).start();

		invalidate();
	}

	public void writeChar() {

		mIsDemoBreak = true;
		mStrokeMode = StrokeMode.writing;

		showChar(null, mCharacter, mFontSize);
		mWriting.reset();
		mWriting.setData(mStrokeDataList, mWriteCanvas, mFillBlackPaint);

		invalidate();
	}

	public int getStrokeNum() {
		return mStrokeNum;
	}

	public void showChar(Canvas canvas, char c, int size) {
		if (mCharacter != c) {
			//
		}
		if (mFontSize != size) {

		}

		if (canvas == null) {
			switch (mStrokeMode) {
			case writing:
				canvas = mWriteCanvas;
				break;
			case demo:
			default:
				canvas = mDemoCanvas;
				break;
			}
		}

		canvas.drawRect(new Rect(0, 0, mFontSize, mFontSize), mFillWhitePaint);

		Util.drawMiRect(canvas, new Rect(0, 0, mFontSize, mFontSize));

		for (StrokeData data : mStrokeDataList) {
			Util.drawStroke(canvas, data, mStrokeGrayPaint);
		}

		if (mStrokeMode == StrokeMode.writing) {
			Util.drawGuideLine(canvas, mStrokeDataList.get(0));
		}

	}

	private void demoStroke(final StrokeData data) {

		int end;
		int size = data.m_posList.size();
		Path path = new Path();

		for (int i = 0; i < size; i += m_DemoStep) {

			end = i + m_DemoStep;
			Path path1 = Util.createPathByPoints(data.m_posList, 0, end, null);
			path.addPath(path1);

			Path path2 = Util.createPathByPoints(data.m_negList, 0, end, null);
			path.addPath(path2);

			if (end > size - 1) {
				end = size - 1;
			}

			// join start and end point.
			Point p = data.m_posList.get(end);
			path.lineTo(p.x, p.y);

			path.setFillType(FillType.EVEN_ODD);
			mDemoCanvas.drawPath(path, mFillBlackPaint);

			postInvalidate();
			path.rewind();

			try {
				Thread.sleep(m_DemoDelay);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

	public void setDemoSpeed(int speed) {
		m_DemoDelay = 10 * (5 - speed);
		if (speed > 2) {
			m_DemoStep = DEFAULT_DEMO_STEP * speed / 2;
		}

	}

	public void setWrtingSensivity(int sensivity) {
		mWriting.setWritingSensivity(sensivity);
	}

	public void setCharsInfo(String strChars, int pos) {
		mStrChars = strChars;
		mCharsIndex = pos;
	}

}
